/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Engine.

    Maxsi Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiEngineContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiEngineContributors.h. For more information please see
	MaxsiEngineContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiEngine.dll
	A linkable version of Maxsi Engine.

	MaxsiIO.h
	A Simple Flexible Async I/O System.

****************************************************************************/
#if !defined(ME_IODisabled) || defined(ME_IOEnabled)
#ifndef MaxsiIO_H
#define MaxsiIO_H

namespace MaxsiEngine
{
	class	ME_Component;
	class	ME_IOConnection;
	class	ME_IOConnectionFeed;
	struct	ME_IOConnectionPointer;
	class	ME_IOConnectionContext;
	struct	ME_IORange;
	class	ME_IOSystem;
	struct	ME_IOThinkRequest;
	class	ME_INT;
	struct	ME_OVERLAPPED;

	typedef unsigned __int64 Speed_t;
	typedef unsigned __int64 Bytes_t;

	STRUCTLINK ME_OVERLAPPED : public OVERLAPPED
	{
		ME_IOSystem*					IOSystem;
		ME_IOConnectionPointer*			IOConnection;
		ME_IOConnectionContext*			IOContext;
		int								BufferType; // ME_BUFFER_TYPE_*
	};

	STRUCTLINK	ME_IOConnectionPointer
	{
		ME_IOConnectionPointer();

		ME_IOConnection*				IOConnection;
		ME_IOConnectionPointer*			Other;
		int								Flags; // Threading: The context's buffer MUST be locked before accessing this.
		Speed_t							Speed; // Bytes per second.
		Bytes_t							Total; // Total Amount of bytes.

		bool							AddSpeed(Speed_t Bytes);
		SIZE_T							GetBytesAvailable(); // Returns the amount of bytes that can be written

										operator ME_IOConnection*();
										operator ME_IOConnection*() const;
										operator ME_IOConnectionPointer*() const;
		bool							operator !() const;
		bool							operator==( ME_IOConnectionPointer &val );
		bool							operator==( ME_IOConnectionPointer *val ) const;
		bool							operator!=( ME_IOConnectionPointer *val ) const;
		const	ME_IOConnectionPointer&	operator=( const ME_IOConnection *Val );
				ME_IOConnection*		operator->() const;
				ME_IOConnection*		operator->();
	};
	
	// If Start == 0 && End == -1 Then output everything under the name FileName
	// Else If Start == -1 && End == -1 Then output nothing, but do request everything
	// Else output the range.

	STRUCTLINK	ME_IORange
	{
		Bytes_t		Start;
		Bytes_t		End;
		Bytes_t		Offset;
		char*		FileName;
	};

	#define		ME_IOThinkRequest_Size		31

	STRUCTLINK	ME_IOThinkRequest
	{
		ME_IOThinkRequest();

		ME_IOConnection*	List[ME_IOThinkRequest_Size];		
		ME_IOThinkRequest*	Next; // Should this be used too much, please consider upping ME_IOThinkRequest_Size
	};

	// This connection...
	#define		ME_IO_CTX_PERSONALBUFFER	0x00000000 // ... needs its own buffer and can't share it.
	#define		ME_IO_CTX_FORWARDFRAGMENTS	0x00000001 // ... forwards its output as fragments of its buffer.
	#define		ME_IO_CTX_NOBUFFER			0x00000002 // ... doesn't need a buffer.
	#define		ME_IO_CTX_NOTCONNECTABLE	0x00000003 // ... cannot be connected to.
 
	// The write succeded. You may continue whatever you were doing.
	#define		ME_IO_O_WROTE				0x00000000

	// The write failed. Please stop what you are doing, and wait until
	// we can process your request and retry. Your OnThink() function will be
	// called when your request might be possible, you just keep on trying
	// till you run out of cake.
	#define		ME_IO_O_RETRY				0x00000001 

	// The write failed. Something horrible happened and the connection
	// chain must be shut down.
	#define		ME_IO_O_HORRIBLEEVENT		0x00000002

	// The write failed. Something was wasn't supposed to be true was true.
	#define		ME_IO_O_ASSERTIONFAILURE	0x00000003

	// The write failed. This connection did not have a large enough buffer
	// to handle the buffer, at all.
	#define		ME_IO_O_NOTENOUGHMEMORY		0x00000004


	// Call these with ScheduleNextThink(ME_IO_THINK_*);

	// The code haven't requested the next think, if this is unset when
	// the think function ends, there is a bug.
	#define		ME_IO_THINK_NONE			0x00000000	

	// A completion callback has been requested from Windows.
	#define		ME_IO_THINK_WINCALLBACK		0x00000001	

	// A completion callback has been requested from another ME_IO* service.
	#define		ME_IO_THINK_MECALLBACK		0x00000002	

	// Wait NextThinkParam miliseconds then call me. (TODO: Obsolete?)
	#define		ME_IO_THINK_WAIT			0x00000003	

	// Stop thinking!
	#define		ME_IO_THINK_DONT			0x00000004

	// Upon the think's completion, think again.
	#define		ME_IO_THINK_THINKAGAIN		0x00000005	

	// Something awful happened and we are about to be shut down
	#define		ME_IO_THINK_HOLYSHI_		0x00000006	

	// No data has arrived yet, just wait.
	#define		ME_IO_THINK_NO_DATA_YET		0x00000007



	// Flags defining some IO states
	
	#define		ME_IO_FLAG_CHOKED			0x00000001


	#define		ME_IO_RANGES_UNSUPPORTED	0x00000000 // Sorry, this protocol does not support doing this
	#define		ME_IO_RANGES_SUPPORTED		0x00000001 // Oh yeah, we can do this, if you want to.
	#define		ME_IO_RANGES_UNDEFINED		0x00000000 // Uhm, I'm afraid that ranges doesn't make sense in this context.


	#define		ME_IO_ERROR_UNHANDLED		0x00000000
	#define		ME_IO_ERROR_HANDLED			0x00000001

	CLASSLINK ME_INT
	{
	public:
				int		Value;
		virtual	int		Get();
		virtual	void	Set			(int NewValue);
						operator	int() const;
				void	operator=	(int NewValue);
				void	operator&=	(int NewValue);
				void	operator|=	(int NewValue);
				int		operator=	( const ME_INT &Val );
	};

	CLASSLINK ME_IOConnectionContext
	{
	public:
				ME_IOConnectionContext();
		virtual	~ME_IOConnectionContext();

	public:
		ME_Buffer*		Buffer;

	public:
		virtual	bool	AddConnection(ME_IOConnection* Connection);
		virtual	bool	RemoveConenction(ME_IOConnection* Connection);

	public:
		bool operator==( const ME_Buffer* Other );

	};

	CLASSLINK ME_Component
	{
	public:
		ME_Component();
		~ME_Component();

	};
	
	CLASSLINK ME_IOConnection : public ME_Component
	{
	public:
		ME_DECLARE_ABSTRACT_COMPONENT(ME_IOConnection, ME_Component);

	public:
				ME_IOConnection();
		virtual	~ME_IOConnection();
	
	public:
		char*						ResourceName;

	public:
		ME_IOSystem*				System;
		ME_IOConnectionContext*		IContext; // The context of our input.
		ME_IOConnectionContext*		OContext; // The context of our output.
		SIZE_T						ConnectionId;

	// Normally one would expect only an Output and Input channel, but instead
	// we have designed a system where the location we send our output might not
	// be where we receive our input. (That II != OO)
	public:
		ME_IOConnectionPointer		II; // From where we receive input.
		ME_IOConnectionPointer		IO; // To where we forward our input.
		ME_IOConnectionPointer		OI; // From where we receive output.
		ME_IOConnectionPointer		OO; // To where we send our output.

	public:
		virtual	SIZE_T				IBufferRequired(); // Minimum amount of buffer space 'required' to function.
		virtual	SIZE_T				OBufferRequired(); // Minimum amount of buffer space 'required' to function.
		virtual	SIZE_T				IBufferSuggested(); // Recommended amount of buffer space.
		virtual	SIZE_T				OBufferSuggested(); // Recommended amount of buffer space.
		virtual	int					IPolicy(); // The flag ME_IO_CTX_* for the this connection.
		virtual	int					OPolicy(); // The flag ME_IO_CTX_* for the this connection.
		virtual int					ORangePolicy(); // Specifies if this connection has defined behavior for only requesting ranges of data
		virtual int					IRangePolicy(); // Specifies if this connection has defined behavior for only requesting ranges of data
		virtual	bool				Connect(ME_IOConnectionPointer* Remote);
		virtual	bool				IIConnect(ME_IOConnectionPointer* Remote);
		virtual	bool				OOConnect(ME_IOConnectionPointer* Remote);
		virtual	bool				Connected(ME_IOConnectionPointer* Connection);
		virtual	bool				IIConnected();
		virtual	bool				IOConnected();
		virtual	bool				OIConnected();
		virtual	bool				OOConnected();
		virtual	bool				IIDisconnect();
		virtual	bool				OODisconnect();
		virtual	bool				IIDisconnected();
		virtual	bool				IODisconnected();
		virtual	bool				OIDisconnected();
		virtual	bool				OODisconnected();
		virtual	bool				Disconnect(ME_IOConnectionPointer* Connection); // When Connection should be disconnected
		virtual	bool				Disconnect(); // Disconnects II, IO, OI, and OO.
		virtual	bool				Disconnected(ME_IOConnectionPointer* Connection); // When Connection has been disconnected
		virtual	bool				Disconnected(); // When II, IO, OI, and OO has disconnected.
	
	public:
		List						ORanges;
		List						IRanges;
		virtual	bool				AddORange(ME_IORange* Range);
		virtual	bool				AddIRange(ME_IORange* Range);
		virtual	ME_IORange*			GetORangeId(SIZE_T RangeId);
		virtual	ME_IORange*			GetIRangeId(SIZE_T RangeId);
		virtual	ME_IORange*			GetORangePos(Bytes_t Position);
		virtual	ME_IORange*			GetIRangePos(Bytes_t Position);
		virtual	Bytes_t				GetONextRangePos(Bytes_t Position);
		virtual	Bytes_t				GetINextRangePos(Bytes_t Position);

	public:
		virtual	SIZE_T				GetIBytesAvailable(); // Returns the amount of bytes that can be sent through the input-channel at the moment
		virtual	SIZE_T				GetOBytesAvailable(); // Returns the amount of bytes than can be sent through the output-channel at the moment

	// These functions return a ME_IO_O_* flag to describe what action needs to be taken.
	public:
		virtual	int					SendInput(char* Buffer, SIZE_T BufferLen); 
		virtual	int					SendInput(ME_Buffer* Buffer);
		virtual	int					SendOutput(char* Buffer, SIZE_T BufferLen);
		virtual	int					SendOutput(ME_Buffer* Buffer);

	// Do not call these - call the above functions, they will redirect the calls to these functions.
	// This allows us to easy add debug layers between the connections, without complicating matters
	// too much. :-)
	public:
		virtual	int					OnInput(char* Buffer, SIZE_T BufferLen); 
		virtual	int					OnInput(ME_Buffer* Buffer);
		virtual	int					OnOutput(char* Buffer, SIZE_T BufferLen);
		virtual	int					OnOutput(ME_Buffer* Buffer);

	// Error-handling functions. ;-D
	public:
		// IOUnavailable marks that this connection has failed - and it cannot handle this situation itself.
		// The connection will be shut down, as well as any connections in the connection chain,
		// unless they catch the error and handle it.
		virtual	int					IOUnavailable(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		// IOWarning sends a non-critical warning and the connection will remain.
		virtual	int					IOWarning(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		// Does nothing to the connection, but sends a comment that could be used for debugging.
		virtual	int					IOComment(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		// Shuts down the connection because something unlikely happened.
		virtual	int					IOAssertionFailure(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
	
	//#define IOAssertionFailure(Connection, Error, ErrorMessage) ME_UNDEFINED_BEHAVIOR_DEBUG(ErrorMessage); IOAssertionFailure(Connection, Error, ErrorMessage);

	// Like the Error-handling functions above! These are called when we have to handle the shutdown of another
	// connection and we have to decide what to do, or if we should also fail.
	// Returns ME_IO_ERROR_* values.
	public:
		virtual	int					OnIOUnavailable(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		virtual	int					OnIOWarning(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		virtual	int					OnIOComment(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		virtual	int					OnIOAssertionFailure(ME_IOConnectionPointer* Connection, DWORD Error, char* ErrorMessage);
		
	public:		
		bool						IsThinking;
		CRITICAL_SECTION			Lock_Think;
		virtual	bool				LockThink();
		virtual	bool				UnlockThink();

	public:
		virtual	bool				Think();
		virtual	bool				OnThink(); // Please call Think(), not OnThink(), for debugging and threading reasons.
		virtual	bool				RequestThink();

	public:
		virtual	bool				EvaluateConnection(); // Return true if the connection still has something to do - return false if it has nothing to do and could be shut down with no harm.

	public:
		// Do not access these without having locked Lock_Think.
		ME_INT						State;
		ME_INT						Mode;
		ME_INT						Command;
		int							NextThink;

	public:		
		virtual	bool				ScheduledNextThink(int NextThinkType, int Line, char* Function, char* FileName);

	public:
		virtual	void				DoneIO(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred, LPOVERLAPPED lpOverlapped);

	};

	CLASSLINK ME_IOSystem
	{
	public:
				ME_IOSystem();
		virtual	~ME_IOSystem();

	public:		
		CRITICAL_SECTION			Lock_Connections;
		CRITICAL_SECTION			Lock_ThinkList;

	public:
		List						Connections;

	public:
		virtual	bool				Lock();
		virtual	bool				Unlock();

	public:
		ME_IOThinkRequest			ThinkList; // Suppports, by default, up to ME_IOThinkRequest_Size requested pending thinks, but can be expanded using additional memory allocations, if required.
		virtual	bool				RequestThink(ME_IOConnection* Thinkee);
		virtual	bool				PerformThreadThinking();

	public:
		virtual	ME_IOConnection*	CreateConnection(char* ConnectionType);

	public:
		virtual	bool				ConnectionEstablished(ME_IOConnectionPointer* First, ME_IOConnectionPointer* Second);
		virtual	bool				ConnectionInvalidated(ME_IOConnectionPointer* First, ME_IOConnectionPointer* Second);
		virtual	bool				ConnectionTerminated(ME_IOConnectionPointer* First, ME_IOConnectionPointer* Second);

	public:
		virtual	ME_IOConnection*	GetConnection(SIZE_T ConnectionId);
		virtual	SIZE_T				NumConnections();
		virtual	SIZE_T				AddConnection(ME_IOConnection* Connection); 

	public:
		virtual	bool				AddISpeed(Speed_t Amount);
		virtual	bool				AddOSpeed(Speed_t Amount);

	public:
		virtual	bool				Feed(ME_IOConnectionPointer* Connection, char* Buffer, SIZE_T BufferLen, ME_IORange* Ranges = NULL, SIZE_T NumRanges = 0);

	public:
		virtual	bool				CreateConnections();
		virtual	bool				DeleteConnections();

		virtual	bool				MainLoop();

	};

	CLASSLINK ME_IOConnectionFeed : public ME_IOConnection
	{
	public:
		ME_DECLARE_COMPONENT("ME_IOConnectionFeed", ME_IOConnectionFeed, ME_IOConnection);

	public:
				ME_IOConnectionFeed();
		virtual	~ME_IOConnectionFeed();

	public:
		virtual	SIZE_T				IBufferRequired(); // Minimum amount of buffer space 'required' to function.
		virtual	SIZE_T				OBufferRequired(); // Minimum amount of buffer space 'required' to function.
		virtual	SIZE_T				IBufferSuggested(); // Recommended amount of buffer space.
		virtual	SIZE_T				OBufferSuggested(); // Recommended amount of buffer space.
		virtual	int					IPolicy(); // The flag ME_IO_CTX_* for the this connection.
		virtual	int					OPolicy(); // The flag ME_IO_CTX_* for the this connection.
		virtual int					ORangePolicy(); // Specifies if this connection has defined behavior for only requesting ranges of data
		virtual int					IRangePolicy(); // Specifies if this connection has defined behavior for only requesting ranges of data
	
	public:
		virtual	bool				AddORange(ME_IORange* Range);

	public:
		virtual	bool				OnThink(); // Please call Think(), not OnThink(), for debugging and threading reasons.

	public:
		virtual	bool				EvaluateConnection(); // Return true if the connection still has something to do - return false if it has nothing to do and could be shut down with no harm.

	public:
		char*	OutputBuffer;
		SIZE_T	OutputBufferLength;
		SIZE_T	OutputBufferSent;
	
	};

	LINK	void CALLBACK			ME_IOConnection__DoneIO(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred, LPOVERLAPPED lpOverlapped);
	LINKCPP	char*					GetWinErrorMessage(DWORD ErrorCode);
	LINK	bool					SyncWriteFile(char* FileName, char* Buffer, SIZE_T BufferLen, Bytes_t Offset);
	
	LINK	DWORD_PTR				GetNumberOfLogicalCPUs();
	LINKCPP	bool					MaxsiIOCreateWorkerThreads();

}


#endif
#endif
